package com.scwang.wave;

import com.scwang.wave.util.AnimatorValueUtil;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;

import java.util.ArrayList;
import java.util.List;

import static java.lang.Float.parseFloat;

/**
 * 多重水波纹
 * Created by SCWANG on 2017/12/11.
 */
public class MultiWaveHeader extends ComponentContainer implements Component.LayoutRefreshedListener, Component.DrawTask {

    /**
     * * The constant MultiWaveHeader_mwhWaveHeight
     */
    private static final String MultiWaveHeader_mwhWaveHeight = "mwhWaveHeight";
    /**
     * * The constant MultiWaveHeader_mwhStartColor
     */
    private static final String MultiWaveHeader_mwhStartColor = "mwhStartColor";
    /**
     * * The constant MultiWaveHeader_mwhCloseColor
     */
    private static final String MultiWaveHeader_mwhCloseColor = "mwhCloseColor";
    /**
     * * The constant MultiWaveHeader_mwhIsRunning
     */
    private static final String MultiWaveHeader_mwhIsRunning = "mwhIsRunning";
    /**
     * * The constant MultiWaveHeader_mwhEnableFullScreen
     */
    private static final String MultiWaveHeader_mwhEnableFullScreen = "mwhEnableFullScreen";
    /**
     * * The constant MultiWaveHeader_mwhColorAlpha
     */
    private static final String MultiWaveHeader_mwhColorAlpha = "mwhColorAlpha";
    /**
     * * The constant MultiWaveHeader_mwhProgress
     */
    private static final String MultiWaveHeader_mwhProgress = "mwhProgress";
    /**
     * * The constant MultiWaveHeader_mwhVelocity
     */
    private static final String MultiWaveHeader_mwhVelocity = "mwhVelocity";
    /**
     * * The constant MultiWaveHeader_mwhGradientAngle
     */
    private static final String MultiWaveHeader_mwhGradientAngle = "mwhGradientAngle";
    /**
     * * The constant MultiWaveHeader_mwhCornerRadius
     */
    private static final String MultiWaveHeader_mwhCornerRadius = "mwhCornerRadius";
    /**
     * * The constant MultiWaveHeader_mwhWaves
     */
    private static final String MultiWaveHeader_mwhWaves = "mwhWaves";
    /**
     * * The constant MultiWaveHeader_mwhShape
     */
    private static final String MultiWaveHeader_mwhShape = "mwhShape";
    /**
     * The constant M path
     */
    protected Path mPath;
    /**
     * The constant M shape
     */
    protected ShapeType mShape = ShapeType.Rect;
    /**
     * The constant M paint
     */
    protected Paint mPaint = new Paint();
    protected Paint mPaint2 = new Paint();
    /**
     * The constant M matrix
     */
    protected Matrix mMatrix = new Matrix();

    /**
     * The constant Mlt wave
     */
    protected List<Wave> mltWave = new ArrayList<>();
    /**
     * The constant M corner radius
     */
    protected float mCornerRadius;
    /**
     * The constant M wave height
     */
    protected int mWaveHeight;
    /**
     * The constant M start color
     */
    protected int mStartColor;
    /**
     * The constant M close color
     */
    protected int mCloseColor;
    /**
     * The constant M gradient angle
     */
    protected int mGradientAngle;
    /**
     * The constant M is running
     */
    protected boolean mIsRunning;
    /**
     * The constant M enable full screen
     */
    protected boolean mEnableFullScreen;
    /**
     * The constant M velocity
     */
    protected float mVelocity;
    /**
     * The constant M color alpha
     */
    protected float mColorAlpha;
    /**
     * The constant M progress
     */
    protected float mProgress;
    /**
     * The constant M cur progress
     */
    protected float mCurProgress;
    /**
     * The constant M last time
     */
    protected long mLastTime = 0;
    /**
     * The constant Rebound animator
     */
    protected ValueAnimator reboundAnimator;
    /**
     * The constant Refresh runnable
     */
    /*Runnable refreshRunnable = new Runnable() {
        @Override
        public void run() {
            invalidate();
        }
    };*/
    /**
     * The constant Event handler
     */
   // private EventHandler eventHandler;
    /**
     * The constant Width
     */
    //private int width;
    /**
     * The constant Height
     */
   // private int height;
    /**
     * The constant Animator value
     */
    private AnimatorValue animatorValue;

    //private Boolean  isrunning = false;//默认停止

    /**
     * Multi wave header
     *
     * @param context context
     */
    public MultiWaveHeader(Context context) {
        this(context, null, null);
    }

    /**
     * Multi wave header
     *
     * @param context context
     * @param attrs   attrs
     */
    public MultiWaveHeader(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    /**
     * Multi wave header
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr def style attr
     */
    public MultiWaveHeader(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mPaint.setAntiAlias(true);
        mPaint2.setAntiAlias(true);
        waveAnimatorValue = new AnimatorValue();
        waveAnimatorValue.setDuration(1000);
        waveAnimatorValue.setCurveType(Animator.CurveType.LINEAR);
        waveAnimatorValue.setLoopedCount(Integer.MAX_VALUE);
        waveAnimatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                setWaveHeight((int) animatorValueUtil.getValue(value));
            }
        });
        mWaveHeight = AttrUtils.getIntFromAttr(attrs, MultiWaveHeader_mwhWaveHeight, Util.dp2px(50));
        /*mStartColor = AttrUtils.getColorFromAttr(attrs, MultiWaveHeader_mwhStartColor, 0xFF056CD0);
        mCloseColor = AttrUtils.getColorFromAttr(attrs, MultiWaveHeader_mwhCloseColor, 0xFF31AFFE);*/
        mColorAlpha = AttrUtils.getFloatFromAttr(attrs, MultiWaveHeader_mwhColorAlpha, 1f);
        mVelocity = AttrUtils.getFloatFromAttr(attrs, MultiWaveHeader_mwhVelocity, 1f);
        mGradientAngle = AttrUtils.getIntFromAttr(attrs, MultiWaveHeader_mwhGradientAngle, 45);
        mIsRunning = AttrUtils.getBooleanFromAttr(attrs, MultiWaveHeader_mwhIsRunning, true);
        mEnableFullScreen = AttrUtils.getBooleanFromAttr(attrs, MultiWaveHeader_mwhEnableFullScreen, false);
        mCornerRadius = AttrUtils.getDimensionFromAttr(attrs, MultiWaveHeader_mwhCornerRadius, Util.dp2px(25));
        mShape = ShapeType.values()[AttrUtils.getIntFromAttr(attrs, MultiWaveHeader_mwhShape, mShape.ordinal())];
        mProgress = mCurProgress = AttrUtils.getFloatFromAttr(attrs, MultiWaveHeader_mwhProgress, 1f);
       // eventHandler = new EventHandler(EventRunner.getMainEventRunner());
        setAmplitude(0);
        setWaveHeight(0);
        setTag(AttrUtils.getStringFromAttr(attrs, MultiWaveHeader_mwhWaves,
                "70,25,1.4,1.4,-26\n100,5,1.4,1.2,15\n420,0,1.15,1,-10\n520,10,1.7,1.5,20\n220,0,1,1,-15"));
        setLayoutRefreshedListener(this);
        addDrawTask(this);
    }

    /**
     * Set alpha component int
     *
     * @param color color
     * @param alpha alpha
     * @return the int
     */
    public static int setAlphaComponent(int color, int alpha) {
        if (alpha >= 0 && alpha <= 255) {
            return color & 16777215 | alpha << 24;
        } else {
            return color;
        }
    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    @Override
    public void onRefreshed(Component component) {
       /* width = component.getWidth();
        height = component.getHeight();*/
        if (mltWave.isEmpty()) {
            updateWavePath();
            updateWavePath(getWidth(), getHeight());
        }
        updateShapePath();
        updateWavePath(getWidth(), getHeight());
        updateLinearGradient(getWidth(), getHeight());
        start();
    }

    private AnimatorValue waveAnimatorValue;

    private AnimatorValueUtil animatorValueUtil;

    public void setAmplitude(int amplitude){

        if(animatorValueUtil == null){
            animatorValueUtil = new AnimatorValueUtil();
        }
        animatorValueUtil.ofFloat(amplitude, -amplitude, amplitude, -amplitude, amplitude);
    }

    public void stopWave(){

        if(waveAnimatorValue.isRunning()) {
            waveAnimatorValue.stop();
        }
    }

    public void startWave(){
        if(waveAnimatorValue!=null && waveAnimatorValue.isRunning()) return;

        waveAnimatorValue.start();
    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
 public void onDraw(Component component, Canvas canvas) {

        if (mltWave.size() > 0) {

            if (mPath != null) {
                canvas.save();
                canvas.clipPath(mPath, Canvas.ClipOp.INTERSECT);
            }
            long thisTime = System.currentTimeMillis();
            for (Wave wave : mltWave) {
                mMatrix.reset();
                canvas.save();
                mPaint.getShader().setShaderMatrix(mMatrix);
                canvas.drawPath(wave.path,mPaint);
                canvas.restore();
            }
            mLastTime = thisTime;

            if (mPath != null) {
                canvas.restore();
            }
        }



    }

    /**
     * Update linear gradient *
     *
     * @param width  width
     * @param height height
     */
    private void updateLinearGradient(int width, int height) {
        int startColor = setAlphaComponent(mStartColor, (int) (mColorAlpha * 255));
        int closeColor = setAlphaComponent(mStartColor, (int) (mColorAlpha * 255));
        double viewWidth = width;
        double viewHeight = height * mCurProgress;
        double radius = Math.sqrt(viewWidth * viewWidth + viewHeight * viewHeight) / 2;
        double dy = radius * Math.sin(2 * Math.PI * mGradientAngle / 360);
        double dx = radius * Math.cos(2 * Math.PI * mGradientAngle / 360);
        Point[] points = new Point[]{new Point((int) (viewWidth / 2 - dx), (int) (viewHeight / 2 - dy)), new Point((int) (viewWidth / 2 + dx), (int) (viewHeight / 2 + dy))};
        Color[] colors = new Color[]{new Color(startColor), new Color(closeColor)};
        float[] floats = new float[]{0, 1};
        LinearShader linearShader = new LinearShader(points, floats, colors, Shader.TileMode.CLAMP_TILEMODE);
        mPaint.setShader(linearShader, Paint.ShaderType.LINEAR_SHADER);
        mPaint2.setShader(linearShader, Paint.ShaderType.LINEAR_SHADER);
    }

    /**
     * Update shape path
     */
    protected void updateShapePath() {
        Component thisView = this;
        int viewWidth = thisView.getWidth();
        int viewHeight = thisView.getHeight();
        if (viewWidth > 0 && viewHeight > 0 && mShape != null && mShape != ShapeType.Rect) {
            mPath = new Path();
            switch (mShape) {
                case RoundRect:
                    mPath.addRoundRect(new RectFloat(0, 0, viewWidth, viewHeight), mCornerRadius, mCornerRadius, Path.Direction.CLOCK_WISE);
                    break;
                case Oval:
                    mPath.addOval(new RectFloat(0, 0, viewWidth, viewHeight), Path.Direction.CLOCK_WISE);
                    break;
            }
        } else {
            mPath = null;
        }
    }

    /**
     * Update wave path
     */
    protected void updateWavePath() {
        mltWave.clear();
        if (getTag() instanceof String) {
            String[] waves = getTag().toString().split("\\s+");
            if ("-1".equals(getTag())) {
                waves = "70,25,1.4,1.4,-26\n100,5,1.4,1.2,15\n420,0,1.15,1,-10\n520,10,1.7,1.5,20\n220,0,1,1,-15".split("\\s+");
            } else if ("-2".equals(getTag())) {
                waves = "0,0,1,0.5,90\n90,0,1,0.5,90".split("\\s+");
            }
           for (String wave : waves) {
                String[] args = wave.split("\\s*,\\s*");
                if (args.length == 5) {
                    mltWave.add(new Wave(Util.dp2px(parseFloat(args[0])), Util.dp2px(parseFloat(args[1])),
                            Util.dp2px(parseFloat(args[4])), parseFloat(args[2]), parseFloat(args[3]),
                            mWaveHeight / 2));
                }
            }
        } else {
            mltWave.add(new Wave(Util.dp2px(50), Util.dp2px(0), Util.dp2px(5), 1.7f, 2f, mWaveHeight / 2));
        }
    }

    /**
     * Update wave path *
     *
     * @param width width
     * @param height height
     */
    protected void updateWavePath(int width, int height) {
        for (Wave wave : mltWave) {
            System.out.println(height+" updateWavePath :"+width);
            wave.updateWavePath(width, height, mWaveHeight / 2, mEnableFullScreen, mCurProgress,getContext());
        }
    }

    /**
     * 执行回弹动画
     *
     * @param progress     目标值
     * @param interpolator 加速器
     * @param duration     时长
     */
    protected void animProgress(float progress, int interpolator, int duration) {
        if (mCurProgress != progress) {
            if (reboundAnimator != null) {
                reboundAnimator.cancel();
            }
            reboundAnimator = ValueAnimator.ofFloat(mCurProgress, progress);
            reboundAnimator.setDuration(duration);
            reboundAnimator.setInterpolator(interpolator);
            reboundAnimator.setStateChangedListener(new Animator.StateChangedListener() {
                @Override
                public void onStart(Animator animator) {

                }

                @Override
                public void onStop(Animator animator) {

                }

                @Override
                public void onCancel(Animator animator) {

                }

                @Override
                public void onEnd(Animator animator) {
                    reboundAnimator = null;
                }

                @Override
                public void onPause(Animator animator) {

                }

                @Override
                public void onResume(Animator animator) {

                }
            });
            reboundAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
                @Override
                public void onUpdate(AnimatorValue animatorValue, float value) {
                    updateProgress(value);
                }
            });
            reboundAnimator.start();
        }
    }

    /**
     * Update progress *
     *
     * @param progress progress
     */
    protected void updateProgress(float progress) {
        Component thisView = this;
        mCurProgress = progress;
        updateLinearGradient(thisView.getWidth(), thisView.getHeight());
        if (mEnableFullScreen) {
            for (Wave wave : mltWave) {
                wave.updateWavePath(thisView.getWidth(), thisView.getHeight(), mCurProgress, getContext());
            }
        }
        if (!mIsRunning) {
            invalidate();
        }
    }

    /**
     * Set waves *
     *
     * @param waves waves
     */
    public void setWaves(String waves) {
        setTag(waves);
        if (mLastTime > 0) {
            Component thisView = this;
            updateWavePath();
            updateWavePath(thisView.getWidth(), thisView.getHeight());
        }
        invalidate();
    }

    /**
     * Get wave height int
     *
     * @return the int
     */
    public int getWaveHeight() {
        return mWaveHeight;
    }

    /**
     * Set wave height *
     *
     * @param waveHeight wave height
     */
    private void setWaveHeight(int waveHeight) {
        this.mWaveHeight = Util.dp2px(waveHeight);
        if (!mltWave.isEmpty()) {
            Component thisView = this;
            updateWavePath(thisView.getWidth(), thisView.getHeight());
        }
    }

    /**
     * Get velocity float
     *
     * @return the float
     */
    public float getVelocity() {
        return mVelocity;
    }

    /**
     * Set velocity *
     *
     * @param velocity velocity
     */
    public void setVelocity(float velocity) {
        this.mVelocity = velocity;
    }

    /**
     * Get progress float
     *
     * @return the float
     */
    public float getProgress() {
        return mProgress;
    }

    /**
     * Set progress *
     *
     * @param progress progress
     */
    public void setProgress(float progress) {
        this.mProgress = progress;
        if (!mIsRunning) {
            updateProgress(progress);
        } else {
            animProgress(progress, Animator.CurveType.DECELERATE, 300);
        }
    }

    /**
     * Set progress *
     *
     * @param progress     progress
     * @param interpolator interpolator
     * @param duration     duration
     */
    public void setProgress(float progress, int interpolator, int duration) {
        this.mProgress = progress;
        animProgress(progress, Animator.CurveType.DECELERATE, duration);
    }

    /**
     * Get gradient angle int
     *
     * @return the int
     */
    public int getGradientAngle() {
        return mGradientAngle;
    }

    /**
     * Set gradient angle *
     *
     * @param angle angle
     */
    public void setGradientAngle(int angle) {
        this.mGradientAngle = angle;
        if (!mltWave.isEmpty()) {
            Component thisView = this;
            updateLinearGradient(thisView.getWidth(), thisView.getHeight());
        }
    }

    /**
     * Get start color int
     *
     * @return the int
     */
    public int getStartColor() {
        return mStartColor;
    }

    /**
     * Set start color *
     *
     * @param color color
     */
    public void setStartColor(int color) {
        this.mStartColor = color;
        if (!mltWave.isEmpty()) {
            Component thisView = this;
            updateLinearGradient(thisView.getWidth(), thisView.getHeight());
        }
    }

    /**
     * Set start color id *
     *
     * @param colorId color id
     */
    public void setStartColorId(int colorId) {
        final Component thisView = this;
        setStartColor(Util.getColor(thisView.getContext(), colorId));
    }

    /**
     * Get close color int
     *
     * @return the int
     */
    public int getCloseColor() {
        return mCloseColor;
    }

    /**
     * Set close color *
     *
     * @param color color
     */
    public void setCloseColor(int color) {
        this.mCloseColor = color;
        if (!mltWave.isEmpty()) {
            Component thisView = this;
            updateLinearGradient(thisView.getWidth(), thisView.getHeight());
        }
    }

    /**
     * Set close color id *
     *
     * @param colorId color id
     */
    public void setCloseColorId(int colorId) {
        final Component thisView = this;
        setCloseColor(Util.getColor(thisView.getContext(), colorId));
    }

    /**
     * Get color alpha float
     *
     * @return the float
     */
    public float getColorAlpha() {
        return mColorAlpha;
    }

    /**
     * Set color alpha *
     *
     * @param alpha alpha
     */
    public void setColorAlpha(float alpha) {
        this.mColorAlpha = alpha;
        if (!mltWave.isEmpty()) {
            Component thisView = this;
            updateLinearGradient(thisView.getWidth(), thisView.getHeight());
        }
    }

    /**
     * Start
     */
    public void start() {
        mIsRunning = true;
        mLastTime = System.currentTimeMillis();
        startViewAnim(500);
    }

    /**
     * Stop
     */
    public void stop() {
        mIsRunning = false;
        stopAnim();
    }

    /**
     * Start view anim animator value
     *
     * @param time time
     *
     */
    private void startViewAnim(long time) {

        System.out.println("startViewAnim amplitude =!!!" );
        if(animatorValue != null && animatorValue.isRunning()) return;

        animatorValue = new AnimatorValue();
        animatorValue.setDuration(time);
        animatorValue.setCurveType(Animator.CurveType.LINEAR);
        animatorValue.setLoopedCount(Integer.MAX_VALUE);
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                invalidate();
            }
        });
        animatorValue.start();
    }

    /**
     * Stop anim
     */
    public void stopAnim() {
        if (animatorValue != null) {
            animatorValue.setLoopedCount(0);
            animatorValue.cancel();
            animatorValue.end();
        }
    }

    /**
     * Is running boolean
     *
     * @return the boolean
     */
    public boolean isRunning() {
        return mIsRunning;
    }

    /**
     * Is enable full screen boolean
     *
     * @return the boolean
     */
    public boolean isEnableFullScreen() {
        return mEnableFullScreen;
    }

    /**
     * Set enable full screen *
     *
     * @param fullScreen full screen
     */
    public void setEnableFullScreen(boolean fullScreen) {
        this.mEnableFullScreen = fullScreen;
    }

    /**
     * Get shape shape type
     *
     * @return the shape type
     */
    public ShapeType getShape() {
        return mShape;
    }

    /**
     * Set shape *
     *
     * @param shape shape
     */
    public void setShape(ShapeType shape) {
        this.mShape = shape;
        updateShapePath();
    }
}
